package com.vinjogames.ui;

import java.text.DecimalFormat;
import java.text.NumberFormat;

import javax.microedition.khronos.opengles.GL10;

import android.graphics.PointF;
import android.util.FloatMath;

import com.vinjogames.entities.AttackObject;
import com.vinjogames.entities.Game;
import com.vinjogames.entities.GameObject;
import com.vinjogames.entities.Resource;
import com.vinjogames.entities.Unit;
import com.vinjogames.gfx.Camera;
import com.vinjogames.gfx.Font;
import com.vinjogames.gfx.Gun;
import com.vinjogames.gfx.Image;
import com.vinjogames.gfx.Number;
import com.vinjogames.gfx.Shapes;
import com.vinjogames.util.GameClock;
import com.vinjogames.util.GameConVars;
import com.vinjogames.util.Logger;
import com.vinjogames.util.Physics;
import com.vinjogames.util.Preferences;

public class GUI {

	private int fps;
	private long startFPS;
	private int frameCount;
	private GameClock mClock = GameClock.getInstance();
	private Camera mCamera;
	private Grid mGrid;
	private Game mGame;
	private BuyMenu mBuyMenu;
	private TechMenu mTechMenu;
	private InGameMenu mGameMenu;
	private boolean mInBuyMenu;
	private boolean mInTechMenu;
	private boolean moveable;
	private boolean mAiming;
	private GameObject focus;
	private boolean mLoaded;

	private PressableButton mMoveUnit;
//	private PressableButton mBuyButton;

	private PressableButton mFireButton;
	private PressableButton mAimButton;
	private PressableButton mEndTurnButton;
	private PressableButton mSwitchDirection;
	private PressableButton mActionButton;

	private PressableButton mTechMenuButton;
	private PressableButton mBuyMenuButton;

	private PointF resourceBarBL;
	private PointF resourceBarTR;
	private PointF windLocation;
	private PointF fpsLocation;
	private PointF turnStatusLocation;
	private PointF forceDisplayLocation;
	private PointF angleDisplayLocation;
	private PointF techDisplayLocation;
	private PointF resourceDisplayLocation;

	private PointF worldLoc;
	private PointF glLoc;

	private Image windImage;
	private Image selectionImage;

	//Move to string resource
	private String yourTurn = "Your turn.";
	private String waitingForOpponent = "Waiting for opponent";

	private NumberFormat myFormat = new DecimalFormat("#0.00");
	
	private PointF[] unitButtonLocations;//TODO:  implement this, calcs already done

	private short mTechButtonNum = -5;
	private short mBuyButtonNum = -6;
	
	public GUI(Game game){
		mGame     = game;
		mCamera   = Camera.getInstance();
		mBuyMenu  = new BuyMenu(mGame);
		mTechMenu = new TechMenu(mGame);
		mGameMenu = new InGameMenu(mGame, 6, 4, new PointF(2,1), new PointF(8,6));//Fix magic numbers

		resourceBarBL        = new PointF(0, 5.5f);
		resourceBarTR        = new PointF(10, 6f);
		windLocation         = new PointF(Camera.GL_WIDTH / 2, (Camera.GL_HEIGHT-0.3f));
		fpsLocation          = new PointF((Camera.GL_WIDTH-0.3f)/2, (Camera.GL_HEIGHT-0.3f)/2 );
		worldLoc             = new PointF(0,0);
		glLoc                = new PointF(0,0);
		turnStatusLocation   = new PointF(5,.5f);
		forceDisplayLocation = new PointF(0, (Camera.GL_HEIGHT-0.3f)/2);
		angleDisplayLocation = new PointF(1.5f, (Camera.GL_HEIGHT-0.3f)/2);
		techDisplayLocation  = new PointF((Camera.GL_WIDTH-1.8f), (Camera.GL_HEIGHT-0.3f));
		resourceDisplayLocation    = new PointF((1.8f), (Camera.GL_HEIGHT-0.3f));
		selectionImage 		 = new Image(GameConVars.SELECTION, 2, 2);//TODO:
		windImage 			 = new Image(GameConVars.GRAPHIC_WIND, 2, 1);
		unitButtonLocations  = new PointF[]{new PointF(0,0), new PointF(0,0), new PointF(0,0)};
	}

	public void load(){
		Logger.log("GUI: Load");
		mGrid = new Grid((int)mGame.getMap().getSize(), (int)GameConVars.MAP_WORLD_HEIGHT);

		mMoveUnit        = new PressableButton(new Image(GameConVars.GO, 1f, 1f), GameConVars.GO, new PointF(0,0), new PointF(1,1), false);
		mAimButton       = new PressableButton(new Image(GameConVars.AIM, 1f, 1f), GameConVars.SHOOT, new PointF(8,0), new PointF(9,1), false);
		mFireButton      = new PressableButton(new Image(GameConVars.SHOOT, 1f, 1f), GameConVars.SHOOT, new PointF(8,0), new PointF(9,1), false);
		mActionButton    = new PressableButton(new Image(GameConVars.CAPTURE_RESOURCE, 1f, 1f), GameConVars.CAPTURE_RESOURCE, new PointF(0,0), new PointF(0,0), false);
		mEndTurnButton   = new PressableButton(new Image(GameConVars.ENDTURN, 1f, 1f), GameConVars.ENDTURN, new PointF(Camera.GL_WIDTH-1,0), new PointF(Camera.GL_WIDTH,1), false);
		mSwitchDirection = new PressableButton(new Image(GameConVars.TURNAROUND, 1f, 1f), GameConVars.TURNAROUND, new PointF(8,0), new PointF(9,1), false);
		
		mBuyMenuButton = new PressableButton(new Image(GameConVars.BUTTON, .5f, 2f), mBuyButtonNum, new PointF(0,3), new PointF(.5f,5), false);
		mTechMenuButton = new PressableButton(new Image(GameConVars.BUTTON, .5f, 2f), mTechButtonNum, new PointF(0,1), new PointF(.5f,3), false);
		
//		mBuyButton = new PressableButton(new Image(GameConVars.ARROW,1f,1f), GameConVars.ARROW,	new PointF(0,4), new PointF(1,5), false);
//		mBuyButton.rotate(270f);
		
		mBuyMenu.load();
		mTechMenu.load();
		mGameMenu.load();
		mLoaded = true;
		mAiming = false;
	}

	public void draw(GL10 gl){
		if(!mLoaded) return;
		updateFps();
		focus = mCamera.getFocusedObject();

		//Change to individual debug options later
		if(Preferences.getDebugSetting()) mGrid.draw(gl);
		if(Preferences.getDebugSetting()) Number.drawNumber(fps, gl,fpsLocation, 0.1f);		
		if(Preferences.getDebugSetting()) mCamera.disableBounds(true);
		else mCamera.disableBounds(false);

		if(focus != null && focus instanceof Unit){
			gl.glPushMatrix();
			gl.glLoadIdentity();
			gl.glTranslatef(-Camera.GL_WIDTH/2, -Camera.GL_HEIGHT/2, Camera.Z_DISTANCE);
			if(mGame.isLocalPlayersTurn() && !((Unit)focus).isInAttackMode()){
				mMoveUnit.draw(gl);
			}
			gl.glPopMatrix();

			if(Preferences.getDebugSetting())Number.drawNumber((int) (((Unit)focus).getCurrentForce()*10), gl, forceDisplayLocation, 0.1f);
			if(Preferences.getDebugSetting())Number.drawNumber((int) ((Unit)focus).getGun().getAngle(), gl, angleDisplayLocation, 0.1f);


			if(mGame.isLocalPlayersTurn()){
				drawHighlight(gl, focus.getLocation(), focus.getGLWidth(), focus.getGLHeight());
				
				drawFurthestPoint(gl, (Unit)focus);
				
				updateButtonLocations(focus);

				if(((Unit)focus).isInAttackMode()){
					//1 for now, change to gameconvars.default button widht/height
					mFireButton.update(1/mCamera.getScale(), 1/mCamera.getScale(), unitButtonLocations[2]);
					mFireButton.draw(gl);
				}else{
					mAimButton.update(1/mCamera.getScale(), 1/mCamera.getScale(), unitButtonLocations[2]);
					mAimButton.draw(gl);
					
					mSwitchDirection.update(1/mCamera.getScale(), 1/mCamera.getScale(), unitButtonLocations[0]);
					mSwitchDirection.draw(gl);
					
					if(((Unit)focus).getCaptureableResource() != null){
						mActionButton.update(1/mCamera.getScale(), 1/mCamera.getScale(), unitButtonLocations[1]);
						mActionButton.draw(gl);
					}
				}

			}
		}

		gl.glPushMatrix();
		gl.glLoadIdentity();
		gl.glTranslatef(-Camera.GL_WIDTH/2, -Camera.GL_HEIGHT/2, Camera.Z_DISTANCE);

		mEndTurnButton.draw(gl);

		windImage.draw(gl, Camera.GL_WIDTH/2, Camera.GL_HEIGHT - windImage.getHeight()/2);
		

		if(mInBuyMenu){
			mBuyMenu.draw(gl);
		}

		if(mInTechMenu){
//			mTechMenu.draw(gl);
			mTechMenu.drawTest(gl);
		}

		if(mGame.inMenu()){
			mGameMenu.draw(gl);
		}

//		mBuyButton.draw(gl);		
		
		Shapes.drawSquare(gl, resourceBarTR, resourceBarBL, .75f, .75f, .75f, .75f);

		Font.drawString(gl, Float.toString((int)(Physics.getInstance().getWind() * 100) / 100f), windLocation, 0.5f);
		Font.drawString(gl, "Res: " + mGame.getCurrentPlayer().getResourcesRemaining() + " | " + mGame.getCurrentPlayer().getMaxResources(), resourceDisplayLocation, 0.5f);
		Font.drawString(gl, (mGame.isLocalPlayersTurn() ? yourTurn : waitingForOpponent), turnStatusLocation, .5f);
		Font.drawString(gl, "Tech: " + mGame.getCurrentPlayer().getMyTechnologyTree().getTechBudget() + " | " + GameConVars.PLAYER_TECHNOLOGY_BUDGET, techDisplayLocation, 0.5f);

		mBuyMenuButton.draw(gl);
		mTechMenuButton.draw(gl);
		
		gl.glPopMatrix();
	}

	public void toggleMenu(int menuIndex){
		switch(menuIndex){
		case -1:
			mInBuyMenu = false;
			mInTechMenu = false;
			mTechMenu.reset();
			break;
		case 0:
			mInBuyMenu = false;
			mInTechMenu = true;
			break;
		case 1:
			mInBuyMenu = true;
			mInTechMenu = false;
			mTechMenu.reset();
			break;
		default:
			break;
		}
	}

	private void drawHighlight(GL10 gl, PointF center, float glwidth, float glheight){
		center.set(center.x, center.y+.5f);
		selectionImage.draw(gl, center.x, center.y,1,1,1,.2f);

		//TODO: The Min and Max Angles are in Gun and not the Unit.getMinAttackAngle
		Unit u = mGame.getCurrentUnit();
		if(u != null && u.isInAttackMode()){
			Gun g = u.getGun();
			PointF m = g.calculateMuzzleLocation();
			Shapes.drawArc(gl, GameConVars.UNIT_FORCE_RADIUS + g.getLength(), g.getX(), g.getY(), 25, Gun.MINANGLE + u.getRotation(), Gun.MAXANGLE + u.getRotation(), u.isDirection(), 0.5f, 0, 0, 0.5f, false);
			Shapes.drawVarianceArc(gl, GameConVars.UNIT_FORCE_RADIUS * (u.getCurrentForce() / Unit.MAX_FORCE), m.x, m.y, 25, g.getAngle() + u.getRotation(), u.getAttackVariance()*2, u.isDirection(), 0, 0.5f, 0, 0.5f);
		}
	}

	private void drawFurthestPoint(GL10 gl, Unit u){
//		u.drawShadow(gl, mGame.getMap(), u.getFurthestPoint(mGame.getMap()));
		u.drawShadow(gl, mGame.getMap());
	}

	private void updateButtonLocations(GameObject focus){
		float yOffset = focus.getImage().getHeight() * 0.5f * (1/mCamera.getScale());
		float xOffset = focus.getImage().getWidth()  * 0.5f * (1/mCamera.getScale());
		unitButtonLocations[0].set(focus.getLocation().x - focus.getGLWidth()/2 - xOffset, focus.getLocation().y);//Left
		unitButtonLocations[1].set(focus.getLocation().x,  focus.getLocation().y + focus.getGLHeight() + yOffset);//Up
		unitButtonLocations[2].set(focus.getLocation().x + focus.getGLWidth()/2 + xOffset, focus.getLocation().y);//Right
	}
	
	private void updateFps(){
		if(startFPS == 0){
			startFPS = mClock.getTime();
		}
		if((mClock.getTime() - startFPS) >= 1000){
			startFPS = mClock.getTime();
			fps = frameCount;         
			frameCount = 0;              
		}else{
			frameCount++;
		}
	}

	public boolean isInGunControlArc(AttackObject o, PointF loc){
		// Checks to make sure it's between the muzzle location AND the force radius
		float gunLength = PointF.length(loc.x - o.getGun().getX(), loc.y - o.getGun().getY());
		if(gunLength < o.getGun().getLength() || 
		   gunLength > GameConVars.UNIT_FORCE_RADIUS + o.getGun().getLength()){
			return false;
		}

		// Check to make sure it's in the angle range.
		float currentMaxAngle = o.getRotation() + Gun.MAXANGLE;
		float currentMinAngle = o.getRotation() + Gun.MINANGLE;
		float gunAngle   	  = (o.getGun().getY() - loc.y) / (o.getGun().getX() - loc.x);
		double maxTan   	  = Math.tan(Math.toRadians(currentMaxAngle));
		double minTan 		  = Math.tan(Math.toRadians(currentMinAngle));

		if(o.getDirection()){
			if(loc.x < o.getGun().getX() && currentMaxAngle > 90){
				if(gunAngle > maxTan)
					return false;
			}
			else if(loc.x < o.getGun().getX() || currentMaxAngle > 90){
				if(gunAngle < maxTan)
					return false;
			}
			else {
				if(gunAngle > maxTan)
					return false;
			}

			if(o.getGun().getY() < loc.y && loc.x < o.getGun().getX()){
				if((gunAngle  > minTan))
					return false;
			}
			// Touching under the tank.
			// Should never be able to shoot that this angle (Given ~ 90 Degrees).
			else if(loc.x < o.getGun().getX())
				return false;
			else {
				if(gunAngle  < minTan)
					return false;
			}
			return true;
		}

		// Looking Left
		else{
			if(loc.x > o.getGun().getX() && currentMaxAngle > 90){
				if(gunAngle < -maxTan)
					return false;
			}
			else if(loc.x > o.getGun().getX() || currentMaxAngle > 90){
				if(gunAngle > -maxTan)
					return false;
			}
			else {
				if(gunAngle > maxTan)
					return false;
			}

			if(o.getGun().getY() < loc.y && loc.x > o.getGun().getX()){
				if(gunAngle < minTan)
					return false;
			}
			// Touching under the tank.
			// Should never be able to shoot that this angle (Given ~ 90 Degrees).
			else if(loc.x > o.getGun().getX())
				return false;
			else {
				if(gunAngle > -minTan)
					return false;
			}
			return true;
		}
	}

	public void handleAction(int action, PointF screenLocation){
		worldLoc.set(mCamera.getWorldX(screenLocation.x), mCamera.getWorldY(screenLocation.y));
		glLoc.set(mCamera.getGLX(screenLocation.x), mCamera.getGLY(screenLocation.y));

		AttackObject currentObject = null;
		if(mCamera.getFocusedObject() != null && mCamera.getFocusedObject() instanceof AttackObject){
			currentObject = (AttackObject)mCamera.getFocusedObject();
		}

		//		if(mGame.getGameState() != mGame.IDLE)
		//			return;

		switch(action){
		case GameConVars.INPUT_DRAG:
			Logger.log("[GUI]: Drag");
			if(mGame.getGameState() == Game.DRIVERING)
				if(!mMoveUnit.insideBox(glLoc)){
					mGame.stopUnit(null);
					moveable = false;
					return;
				}
			if(mGame.getGameState() != Game.IDLE)
				return;
			if(currentObject != null && currentObject.isInAttackMode() && isInGunControlArc(currentObject, worldLoc)){
				// TODO: Fix new Declarations.
				PointF a = new PointF(currentObject.getGun().getLocation().x, (float) (currentObject.getGun().getLocation().y + currentObject.getImage().getHeight() * 0.5));
				PointF af = currentObject.getGun().calculateMuzzleLocation();
				//				a.y = (float) (a.y + currentObject.getImage().getHeight() * 0.5);
				PointF b = worldLoc;
				double tempHyp = Math.sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y)); 
				//PointF c = new PointF(b.x,a.y);

				// Need to mod the force.
				float f = FloatMath.sqrt((b.x - af.x) * (b.x - af.x) + (b.y - af.y) * (b.y - af.y));
				if(f > GameConVars.UNIT_FORCE_RADIUS)
					f = GameConVars.UNIT_FORCE_RADIUS;

				f = Unit.MAX_FORCE * (f / GameConVars.UNIT_FORCE_RADIUS);
				f = (f < Unit.MIN_FORCE) ? Unit.MIN_FORCE : f;

				float angle;
				
				//If the attackObject is incapable of changing it's aim then do not change the gun angle.
				if(currentObject.canAim()) {
					// TODO:  !isDirection
					if(currentObject.isDirection()){
						if(currentObject.getRotation() >= 0)
							angle = (float) Math.toDegrees(Math.acos((b.x - a.x)/tempHyp)) - currentObject.getRotation();
						else angle = (float) Math.toDegrees(Math.asin((b.y - a.y)/tempHyp)) - currentObject.getRotation();
					}
					else{
						if(currentObject.getRotation() >= 0){
							angle = (float) (180 - (Math.toDegrees(Math.acos((b.x - a.x)/tempHyp))) - currentObject.getRotation());
						}
						else {
							angle = (float)  ((Math.toDegrees(Math.asin((b.y - a.y)/tempHyp))) - currentObject.getRotation());
						}
					}
					currentObject.getGun().setQuickAngle(angle);
				}
				currentObject.setCurrentForce(f);

				return;
			}
			break;
		case GameConVars.INPUT_TAP:
			Logger.log("[GUI]: TAP");
			if(mGame.getGameState() == Game.PROJECTILING)
				return;
			if(mGame.inMenu() && mGameMenu.insideBox(glLoc)){
				mGameMenu.handleAction(action, glLoc);
				return;
			}

			if(!mGame.isLocalPlayersTurn()) return;

			if(mGame.getGameState() == Game.IDLE){
				if(mBuyMenuButton.insideBox(glLoc)){
					if(mInBuyMenu){
						toggleMenu(-1);
					}
					else{
						toggleMenu(1);
					}
					return;
				}
				if(mTechMenuButton.insideBox(glLoc)){
					if(mInTechMenu){
						toggleMenu(-1);
					}
					else{
						toggleMenu(0);
					}
					return;
				}
//				if(mBuyButton.insideBox(glLoc)){
//					if(mInBuyMenu || mInTechMenu){
//						mBuyButton.rotate(270f);
//						toggleMenu(-1);
//						}
//					else{
//						mInBuyMenu = true;
//						mBuyButton.rotate(90f);
//					}
//					return;
//				}
				if(mInBuyMenu && mBuyMenu.insideBox(glLoc)){
					mBuyMenu.handleAction(action, glLoc);
					return;
				}
				if(mInTechMenu && mTechMenu.insideBox(glLoc)){
					mTechMenu.handleAction(action, glLoc);
					return;
				}
				
				if(mFireButton.insideBox(worldLoc) && currentObject != null && currentObject.isInAttackMode()){
					mGame.shootUnit(null);
					currentObject.exitAttackMode();
					return;
				}
				
				if(mAimButton.insideBox(worldLoc) && currentObject != null && !currentObject.isInAttackMode()){
					currentObject.enterAttackMode();
					return;
				}
				
				if(mSwitchDirection.insideBox(worldLoc)){
					if(currentObject == null || !(currentObject instanceof Unit)) return;
					mGame.turnAround(null);
					return;
				}	
				
				if(mActionButton.insideBox(worldLoc)){
					if(currentObject == null || !(currentObject instanceof Unit)) return;
					Unit u = (Unit)currentObject;
					Resource r = u.getCaptureableResource();
					if(r != null) r.captureResource(mGame.getCurrentPlayer(), 1);//1 will later be u.pointsTowardsCapture()
				}

				if(mEndTurnButton.insideBox(glLoc)){
					toggleMenu(-1);
//					if(mInBuyMenu){
//						mBuyButton.rotate(270f);
//						mInBuyMenu = false;
//					}
//					else if(mInTechMenu){
//						mBuyButton.rotate(270f);
//						mInTechMenu = false;
//					}
					
					mGame.endTurn(null);

					windImage.setWidth(Physics.getInstance().getWind());	
					return;
				}	
				
				if(hasAMenuShown()){
					return;
				}
				
			}

			if((!mInTechMenu && !mInBuyMenu) && mMoveUnit.insideBox(glLoc)){
				if(focus != null)
					if(!((Unit)focus).isInAttackMode()){
						mGame.stopUnit(null);
						return;
					}
			}	

			for(int i = 0; i < mGame.getUnits().length; i++){
				Unit u = mGame.getNextUnit();
				if(u.insideBox(worldLoc)){
					if(currentObject != u){
						mCamera.setFocusedObject(u);
						return;
					}
				}
			}			
			if(!moveable)
				moveable = true;
			if(currentObject != null && isInGunControlArc(currentObject, worldLoc)){
				mAiming = false;
				return;
			}
			else mGame.deselectUnit();
			
			break;
		case GameConVars.INPUT_DOWN:
			Logger.log("[GUI]: DOWN");
			if(!mGame.isLocalPlayersTurn()) return;
			if(mGame.getGameState() == Game.IDLE){
				if(!mAiming){
					if(currentObject != null && isInGunControlArc(currentObject, worldLoc)){
						mAiming = true;
						// TODO: Fix new Declarations.
						PointF a = new PointF(currentObject.getGun().getLocation().x, (float) (currentObject.getGun().getLocation().y + currentObject.getImage().getHeight() * 0.5));
						PointF af = currentObject.getGun().calculateMuzzleLocation();
						//				a.y = (float) (a.y + currentObject.getImage().getHeight() * 0.5);
						PointF b = worldLoc;
						float tempHyp = FloatMath.sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y)); 
						//PointF c = new PointF(b.x,a.y);

						// Need to mod the force.
						float f = FloatMath.sqrt((b.x - af.x) * (b.x - af.x) + (b.y - af.y) * (b.y - af.y));
						if(f > GameConVars.UNIT_FORCE_RADIUS)
							f = GameConVars.UNIT_FORCE_RADIUS;

						f = Unit.MAX_FORCE * (f / GameConVars.UNIT_FORCE_RADIUS);
						if(f < Unit.MIN_FORCE)
							f = Unit.MIN_FORCE;

						float angle;
						// TODO:  !isDirection
						if(currentObject.isDirection()){
							if(currentObject.getRotation() >= 0)
								angle = (float) Math.toDegrees(Math.acos((b.x - a.x)/tempHyp)) - currentObject.getRotation();
							else angle = (float) Math.toDegrees(Math.asin((b.y - a.y)/tempHyp)) - currentObject.getRotation();
						}
						else{
							if(currentObject.getRotation() >= 0){
								angle = (float) (180 - (Math.toDegrees(Math.acos((b.x - a.x)/tempHyp))) - currentObject.getRotation());
							}
							else {
								angle = (float)  ((Math.toDegrees(Math.asin((b.y - a.y)/tempHyp))) - currentObject.getRotation());
							}
						}

						currentObject.getGun().setQuickAngle(angle);
						currentObject.setCurrentForce(f);

						return;
					}
				}
			}
			if((!mInTechMenu && !mInBuyMenu) && mMoveUnit.insideBox(glLoc) && moveable){
				if(currentObject == null || !(currentObject instanceof Unit)) return;
				if(((Unit)focus).isInAttackMode())
					return;
				mGame.moveUnit(null);
				return;
			}
			break;
		case GameConVars.INPUT_UP:
			Logger.log("[GUI]: UP");
			moveable = true;
			mAiming = false;
			if(mGame.getCurrentUnit() != null)
				mGame.getCurrentUnit().getGun().desiredAngle = mGame.getCurrentUnit().getGun().getAngle();
			break;
		}
	}

	public PressableButton getMoveButton(){ return mMoveUnit; }
	public boolean hasAMenuShown(){ return mInBuyMenu || mInTechMenu; }
}
